Visualización de datos

Analizar y visualizar datos en R con ggplot

Visualización

Para entender patrones y relaciones

Para comunicar resultados de forma clara

Para detectar errores o valores atípicos

Porque una imagen resume más que una tabla

Matetja et al

Colores 🎨

En R, los colores se pueden definir por nombre (“red”, “blue”) o por su código hexadecimal

  • “#72874E” → formato #RRGGBB

  • “#72874EFF” → formato #RRGGBBAA, donde AA es la transparencia

Paletas ya hechas

  • RColorBrewer → paletas clásicas para mapas y gráficos
  • viridis → paletas perceptualmente uniformes y accesibles.
  • MetBrewer → paletas inspiradas en obras de arte.
  • peRReo → paletas con inspiración musical y estética moderna.
  • scico → paletas científicamente equilibradas.

scico maps met prr

En R, desde gráficos muy simples

hasta muy complejos

quién habla con quién en “The Office” Código

R base versus ggplot - simple

plot(penguins$bill_length_mm, 
     penguins$bill_depth_mm)

ggplot(penguins, 
       aes(x = bill_length_mm, 
           y = bill_depth_mm)) +
  geom_point()

R base versus ggplot - complejo

ggplot (data = penguins, aes(x = bill_length_mm, y = bill_depth_mm)) +
  geom_point(aes(color = body_mass_g)) +
  scico::scale_color_scico(palette = "bamako", direction = -1) +
  scale_x_continuous(breaks = 3:6 * 10, limits = c(30, 60)) +
  scale_y_continuous(breaks = seq(12.5, 22.5, by = 2.5),
                     limits = c(12.5, 22.5)) + theme_minimal () +
  labs( title = 'Bill Dimensions of Brush-Tailed Penguins (Pygoscelis)',
    subtitle = 'A scatter plot of bill depth versus bill length.',
    caption = 'data = Gorman, Williams & Fraser (2014) PLoS ONE, graph = Cédric Scherer',
    x = 'Bill Length (mm)',
    y = 'Bill Depth (mm)',
    color = 'Body mass (g)'
    ) + theme_minimal ()

cols <- scico(100, palette = "bamako", direction = -1)
color_values <- cols[
  as.numeric(cut(penguins$body_mass_g, breaks = 100))
]

plot(
  penguins$bill_length_mm, penguins$bill_depth_mm,
  col = color_values, pch = 21, bg = color_values,  
  xlim = c(30, 60),  ylim = c(12.5, 22.5),
  xaxt = "n",  yaxt = "n",
  xlab = "Bill Length (mm)",
  ylab = "Bill Depth (mm)",
  main = "Bill Dimensions of Brush-Tailed Penguins (Pygoscelis)",
  sub = "A scatter plot of bill depth versus bill length."
)

# Le falta la leyenda aún

Bases de ggplot

ggplot = Grammar of Graphics (gg) Separa cada componente de un gráfico en componentes individuales, creando distintas capas. Así tenemos resultados más flexibles y personalizables.

  • Data
  • Aesthetics (x, y)
  • Geometries (points, lines, boxplots)
  • Scales (eg. colores)
  • Facets (subplots)
  • Statistics (show means, counts, and other statistical summaries of data)
  • Coordinates
  • Theme (apariencia general del gráfico)

Layers inside ggplot

Ejemplo de capas

Hertz & McNeill 2024

Aesthetics

  • x, y
  • colour: color de los geoms (CONTORNO)
  • fill: el color de dentro de los geoms (RELLENO)
  • group: a qué grupo pertenece un geom
  • shape: la forma de los puntos
  • linetype: tipo de linea (solid, dashed, etc)
  • size: escalar el tamaño
  • alpha: transparencia

RforEcology.com

Geometries

Categórico

  • geom_bar(): bar charts for categorical x axis
  • geom_boxplot(): box and whiskers plot for categorical variables
  • geom_violin(): distribution kernel of data dispersion

Continuo

  • geom_histogram(): histogram for continuous x axis
  • geom_point(): scatterplot
  • geom_line(): lines connecting points by increasing value of x
  • geom_path(): lines connecting points in sequence of appearance

Scales

Scales se puede modificar el tipo de variable, transformarla, tamaño, color, nombre etc.

  • scale_x_continuous() - transformar en continua
  • scale_x_sqrt() - transformar en raíz cuadrada
  • scale_colour_discrete() - añadir colores discretos
  • scale_colour_brewer() - añadir colores de una paleta
  • scale_fill_continuous () - añadir colores de relleno continuos

Facets

  • facet_wrap()
  • facet_grid()

facet_wrap(~ variable) | Divide el gráfico en varios paneles según una sola variable. Coloca los paneles en una cuadrícula flexible (se “envuelven” automáticamente).

facet_grid(fila ~ columna) | Divide los gráficos según dos variables: una define las filas y otra las columnas.

Themes

geeksforgeeks Se pueden hacer personalizados también

Empecemos a visualizar

  • Primero pensar qué queremos visualizar (dibujo?)
  • El orden donde ponemos los argumentos
  • Si van dentro de paréntesis o no
  • dónde va la base de datos a la que hacemos referencia
# Dónde va la base de datos?
ggplot (BASE DE DATOS, aes (x, y, fill, color)) +
    geom_xxx () +
    theme ()

# Si trabajamos con dplyr antes
BASE DE DATOS %>%
    ggplot (aes (x, y, fill, color)) +
    geom_xxx () +
    theme ()

# Si trabajamos con varias bases de datos
ggplot () +
    geom_point (base_de_datos1, aes (x, y, fill, color)) +
    geom_line (base_de_datos2, aes (x, y, fill, color)) +
    theme ()

LA CAPA ‘DATA’

Volvemos con los pingüinos. ggplot = base de datos

ggplot(penguins)

LA CAPA ‘AESTHETICS’

ggplot = base de datos + aesthetics

ggplot(penguins, 
       aes(x = flipper_length_mm))

La base de datos puede antes con el pipe de dplyr

penguins %>%
  ggplot(aes(x = flipper_length_mm))

LA CAPA ‘GEOMETRIC’

ggplot + base de datos + aesthetics + geoms

ggplot(penguins, aes(x = flipper_length_mm)) +
  geom_histogram()

Gráfico de densidad

ggplot(penguins, 
       aes(x = flipper_length_mm)) + 
  geom_density(bw=1)

Gráfico de Barras

Geom_bar por defecto nos cuenta el numero de datos

penguins %>%
  ggplot (aes(x=species)) +
  geom_bar() 

Gráfico de Barras

Para que nos haga la suma, necesitamos “identity”

penguins %>%
ggplot(aes(x=species, y=bill_length_mm)) +
    geom_bar(stat="identity")

Para que haga la media, le decimos que la estadística (stat) es un “summary” con la función “mean”

penguins %>%
ggplot(aes(x = species, y = bill_length_mm)) +
    geom_bar(stat="summary", fun = "mean")

CAMBIAMOS LOS COLORES

Queremos la especie en distintos colores.

Dentro de aesthetics añadimos el argumento fill para cambiar el “relleno” segun el grupo que indiquemos. Nos dará un color ya establecido por ggplot.

penguins %>%
ggplot(aes(x=species, y=bill_length_mm, fill=species)) +
    geom_bar(stat="identity")

¿Qué pasa si modificamos “color”? … mal

penguins %>%
ggplot(aes(x=species, y=bill_length_mm, color=species)) +
    geom_bar(stat="identity")

Añadimos el labs y theme

library(ggplot2)
penguins %>%
  ggplot(aes(x = species, y = bill_length_mm, fill=species)) +
  geom_bar(stat = "identity") +
  guides(fill = FALSE) +
  labs(
    title = "Longitud del pico en mm",
    subtitle = "Suma por especie",
    x = "Especie de pingüino",
    y = "Longitud del pico"
  ) + theme_minimal()

Incluímos una variable más

Añadiendo en “fill” el sexo del pingüino.

penguins %>%
  ggplot(aes(x = species, y = bill_length_mm, fill = sex)) +
  geom_bar(stat = "identity")

Quitamos NAs y añadimos position_dodge() para visualizarlos uno al lado del otro

penguins %>%
  filter(!is.na(sex)) %>%
  ggplot(aes(x = species, y = bill_length_mm, fill = sex)) +
  geom_bar(stat = "identity", position = position_dodge()) 

Colores personalizados

penguins %>%
  filter (!is.na(sex)) %>%
  ggplot (aes(x = species, y = bill_length_mm, fill = sex)) +
  geom_bar (stat = "identity", position = position_dodge()) +
  scale_fill_manual (values = c("#72874EFF", "#FED789FF")) +
  theme_minimal () 

Boxplot

penguins %>% 
  ggplot (aes(x = island, y = bill_length_mm)) + 
  geom_boxplot ()
penguins %>% 
  ggplot (aes(x = island, y = bill_length_mm, fill=island)) + 
  geom_boxplot ()
penguins %>% 
  ggplot (aes(x = island, y = bill_length_mm, color=island)) + 
  geom_boxplot ()

Cuidado con los boxplots!

Seven distributions of data, shown as raw data points (of strip-plots), as box plots, and as violin plots

Fuente

Violin plot

violin_plot <- penguins %>%
  ggplot (aes(x = island, y = bill_length_mm, fill = island)) +
  geom_jitter (size = 3,
               alpha = 0.1,
               fill = "grey") +
  geom_violin (alpha = 0.8) +  guides (fill = FALSE) +
  scale_fill_manual (values = c("#72874EFF", "#FED789FF", "#023743FF")) +
  theme_light() 

violin_plot
#ggsave("images/violin_plot.png", violin_plot, width = 20, height = 20, units = "cm", scale = .5)

Guardar plots con ggsave

  • ggsave(“images/violin_plot.png”, violin_plot, width = 20, height = 20, units = “cm”, scale = .5)

  • ggsave nos permite guardar en “eps”, “ps”, “tex” (pictex), “pdf”, “jpeg”, “tiff”, “png”, “bmp”, “svg” or “wmf”

Violines

Datos continuos

Miramos las correlaciones entre nuestras variables de manera gráfica

library(GGally)

penguins %>% 
  ggpairs(columns = 3:5, ggplot2::aes(colour=species)) 

Scatterplots

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm)) + 
  geom_point() 

Plotear texto

geom_text()

ggplot(penguins, aes(x = flipper_length_mm, y = bill_length_mm)) +
  geom_text(aes(label = 'pingus')) +
  theme_minimal()

Cuidado con el argumento color y fill

Hay que estar atentos dónde están colocados Si van dentro de aes() tiene que ser una variable y no un color per se.

penguins %>% 
  ggplot () + 
  geom_point(aes(x = flipper_length_mm, y = bill_length_mm, color="blue")) 

Aquí bien

Fuera de aesthetics sí puede ser un color

penguins %>% 
  ggplot () + 
  geom_point(aes(x = flipper_length_mm, y = bill_length_mm), color="blue") 

Color

Dentro de aesthetics tiene que ser una variable de nuestra bbdd

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = island)) + 
  geom_point() + theme_light() 

LA CAPA ‘SCALES’

Color categórico

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = island)) + 
  geom_point() + 
  scale_colour_manual(values=c("#72874EFF", "#FED789FF", "#023743FF")) +
  theme_light() 

Color continuo

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = bill_length_mm)) + 
  geom_point() + theme_light() 

Gradientes de color

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = bill_length_mm)) + 
  geom_point() +
  scale_color_gradient(low="#00AFBB", high="#E7B800")+
  theme_light() 

Colores continuos de paletas (viridis)

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = bill_length_mm)) + 
  geom_point() +
  scale_color_viridis_c(option = "plasma") + 
  theme_light() 

Colores continuos de paletas (RColorBrewer)

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = bill_length_mm)) + 
  geom_point() +
  scale_color_distiller(palette = "Spectral") +  # otras: "RdYlBu", "YlGnBu", "PuOr", etc.
  theme_light() 

Cambiar el tamaño del punto

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, size = body_mass_g, color=body_mass_g)) +
  scale_color_gradient(low="orange",high="black")+
  geom_point(alpha=0.8) + theme_light() 

Otras visualizaciones de los mismos datos

Densidades en dos dimensiones

# Show the contour only
penguins %>% 
ggplot (aes(x = flipper_length_mm, y = bill_length_mm))+
  geom_density_2d(color="black") + theme_minimal ()

LA CAPA STATISTICS

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm)) + 
  geom_point() + geom_smooth()
# Method = "loess"

Modelos lineales

Podemos visualizar cómo quedaría un modelo lineal

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm)) + 
  geom_point() + theme_light() + geom_smooth(method = "lm")

Modelos lineales con interacción

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = island)) +
  geom_point () +
  scale_colour_manual (values=c("lightblue", "purple", "pink")) +
  theme_light() + geom_smooth(method = "lm")

LA CAPA ‘FACETS’

Dividir por especie

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = island)) + 
  scale_colour_manual (values=c("lightblue", "purple", "pink")) +
  geom_point() + theme_light() +  facet_wrap(~species, scales = "free")

Dividir por especie y por isla

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = island)) + 
  scale_colour_manual (values=c("lightblue", "purple", "pink")) +
  geom_point() + theme_light() +  facet_wrap(species~island, scales = "free")

facet grid por pares

penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, colour = island)) + 
  scale_colour_manual (values=c("lightblue", "purple", "pink")) +
  geom_point() + theme_light() +  facet_grid(species~island, scales = "free")

Patchwork

Guardamos los ggplots como objetos y los “sumamos”

library(patchwork)

p1 <- penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, color = species, fill=species)) +
  geom_point(alpha=.5) + theme_light()  + geom_smooth(method = "lm")

p2 <- penguins %>% 
  ggplot (aes(fill = species, x = bill_length_mm)) + 
  geom_density(alpha=.5) + theme_light() + coord_flip()

p1 + p2 

o “separamos”

p1 <- penguins %>% 
  ggplot (aes(x = flipper_length_mm, y = bill_length_mm, color = species, fill=species)) +
  geom_point(alpha=.5) + theme_light()  + geom_smooth(method = "lm")

p2 <- penguins %>% 
  ggplot (aes(fill = species, x = flipper_length_mm)) + 
  geom_density(alpha=.5) + theme_light() 

p1 / p2 

Mapas I

Mapa del crecimiento urbano en Valencia Código

Mapas II

Apicultores en Alemania Código

Nuestros datos georeferenciados

library(rgbif)
library(ggplot2)
library(rnaturalearth)
library(sf)

datos <- occ_search(scientificName = "Lynx pardinus", limit = 100)
df <- datos$data
world <- ne_countries(scale = "medium", returnclass = "sf")

# Plot
ggplot() +
  geom_sf(data = world, fill = "grey90", color = "grey30") +
  geom_point(data = df, aes(x = decimalLongitude, y = decimalLatitude),
             color = "darkred", size = 2, alpha = 0.7) +
  coord_sf() +
  theme_minimal()
# Basic map
iberian_peninsula <- ne_countries(country=c("spain", "portugal"))
ggplot() +
  geom_sf(data = iberian_peninsula, fill = "grey99", color = "grey30") +
  geom_point(data = df, aes(x = decimalLongitude, y = decimalLatitude),
             color = "darkgreen", size = 2, alpha = 0.7) +
  coord_sf() +
  theme_minimal()

Mapa plantas

library(FloraIberica)
map_distribution(genus = "Lavandula", species = "stoechas", size = 0.9)

gg cosas

example ggimage R logo

example emogg R logo

gganimate

ggDNAvis

It is an R package that uses ggplot2 to visualise genetic data

library(ggDNAvis)
sequence <- paste(c(rep("GGC", 72), rep("GGAGGAGGCGGC", 15)), collapse = "")

visualise_single_sequence(
    sequence = sequence,
    sequence_colours = sequence_colour_palettes$bright_pale,
    background_colour = "white",
    line_wrapping = 60,
    spacing = 1,
    margin = 0.5,
    sequence_text_colour = "black",
    sequence_text_size = 1,
    index_annotation_colour = "darkred",
    index_annotation_size = 1,
    index_annotation_interval = 1,
    index_annotation_vertical_position = 1/3,
    outline_colour = "black",
    outline_linewidth = .4,
    outline_join = "mitre",
    return = TRUE,
    render_device = ragg::agg_png,
    pixels_per_base = 100
)

code ggDNAvis

Bibliografía

Valentina de Filippo Tips tables

ggbasics

book

grphs

geels

Ejercicios

  1. Haz el violin_plot de antes pero los jitter son del color de la isla.
penguins %>% 
  ggplot (aes(x = island, y = bill_length_mm, fill = island)) + 
  geom_jitter (size=3, alpha=0.1, fill="grey") +
  geom_violin(alpha=0.8) +  guides (fill= FALSE)+
  scale_fill_manual(values=c("#72874EFF", "#FED789FF", "#023743FF")) +
  theme_light() + theme(rect = element_rect(fill = "transparent"))
  1. Lee la base de datos de “cobertura.csv”
  • Haz un scatterplot comparando biomasa y cobertura, señalando las diferencias entre sequia o lluvia.
  • Haz un boxplot por los dos tipos de sequía y la cobertura de árboles

Resolvemos los ejercicios

penguins %>% 
  ggplot (aes(x = island, y = bill_length_mm, fill = island)) + 
  geom_jitter (aes(color=island), size=2, alpha=0.9) +
  scale_color_manual(values=c("#72874EFF", "#FED789FF", "#023743FF")) +
  geom_violin (alpha=0.8) +  guides (fill= FALSE)+
  scale_fill_manual(values=c("#72874EFF", "#FED789FF", "#023743FF")) +
  theme_light() + theme(rect = element_rect(fill = "transparent"))
library(here)
cobertura <- read.csv(here("data/cobertura.csv"))
head(cobertura)
  X  biomasa cobertura sequia
1 1 44.99793  18.55938   seco
2 2 24.05575  24.14701   seco
3 3 62.48798  68.89674   seco
4 4 47.19165  30.01684   seco
5 5 52.97670  31.25460   seco
6 6 54.23581  38.79990   seco
ggplot(cobertura) +
  geom_point(aes(x=biomasa, y=cobertura, color=sequia))

ggplot(cobertura) +
  geom_boxplot(aes(x=biomasa, y =cobertura, group=sequia))

tip: sin leyenda guides (fill=FALSE)

penguins %>%
ggplot(aes(x=species, y=bill_length_mm, fill=species)) +
    geom_bar(stat="identity")+
    guides(fill=FALSE)